<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <title>C&#43;&#43;编译期反射——以AOP为例 - L_B__</title><meta name="referrer" content="no-referrer">
<meta name="description" content="C&#43;&#43;编译期反射——以AOP为例"><meta property="og:title" content="C&#43;&#43;编译期反射——以AOP为例" />
<meta property="og:description" content="C&#43;&#43;编译期反射——以AOP为例" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" /><meta property="og:image" content="https://acking-you.github.io/logo.png"/><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2022-11-25T00:00:00+00:00" />
<meta property="article:modified_time" content="2022-11-25T00:00:00+00:00" />

<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://acking-you.github.io/logo.png"/>

<meta name="twitter:title" content="C&#43;&#43;编译期反射——以AOP为例"/>
<meta name="twitter:description" content="C&#43;&#43;编译期反射——以AOP为例"/>
<meta name="application-name" content="FeelIt">
<meta name="apple-mobile-web-app-title" content="FeelIt"><meta name="theme-color" content="#ffffff"><meta name="msapplication-TileColor" content="#da532c"><link rel="canonical" href="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" /><link rel="prev" href="https://acking-you.github.io/posts/go%E4%B8%9A%E5%8A%A1%E4%BB%A3%E7%A0%81%E4%B8%80%E9%94%AE%E9%80%86%E5%90%91%E7%94%9F%E6%88%90/" /><link rel="next" href="https://acking-you.github.io/posts/%E6%AF%94%E8%BE%83avl%E6%A0%91%E5%92%8C%E7%BA%A2%E9%BB%91%E6%A0%91%E7%9A%84%E6%80%A7%E8%83%BD%E5%B7%AE%E5%BC%82/" /><link rel="stylesheet" href="/css/page.min.css"><link rel="stylesheet" href="/css/home.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "C++编译期反射——以AOP为例",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/acking-you.github.io\/posts\/c\u002b\u002b%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B\/"
        },"genre": "posts","keywords": "C\u002b\u002b编译期反射——以AOP为例","wordcount":  3628 ,
        "url": "https:\/\/acking-you.github.io\/posts\/c\u002b\u002b%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B\/","datePublished": "2022-11-25T00:00:00+00:00","dateModified": "2022-11-25T00:00:00+00:00","publisher": {
            "@type": "Organization",
            "name": "作者"},"author": {
                "@type": "Person",
                "name": "作者"
            },"description": "C++编译期反射——以AOP为例"
    }
    </script></head><body data-header-desktop="auto" data-header-mobile="auto"><script>(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('auto' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : 'auto' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="L_B__">L_B__</a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> 文章 </a><a class="menu-item" href="/tags/"> 标签 </a><a class="menu-item" href="/categories/"> 分类 </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-desktop">
                        <a href="#" class="search-button search-toggle" id="search-toggle-desktop" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-desktop" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="L_B__">L_B__</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="搜索文章标题或内容..." id="search-input-mobile">
                        <a href="#" class="search-button search-toggle" id="search-toggle-mobile" title="搜索">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="#" class="search-button search-clear" id="search-clear-mobile" title="清空">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="#" class="search-cancel" id="search-cancel-mobile">
                        取消
                    </a>
                </div><a class="menu-item" href="/posts/" title="">文章</a><a class="menu-item" href="/tags/" title="">标签</a><a class="menu-item" href="/categories/" title="">分类</a><div class="menu-item"><a href="javascript:void(0);" class="theme-switch" title="切换主题">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div></div>
    </div>
</header><div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div><main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">目录</h2>
            <div class="toc-content" id="toc-content-auto"></div>
        </div><article class="page single" data-toc="disable"><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="https://img-blog.csdnimg.cn/img_convert/7213ded01d686aab797968b2a6064358.png#pic_center"
        data-srcset="https://img-blog.csdnimg.cn/img_convert/7213ded01d686aab797968b2a6064358.png#pic_center, https://img-blog.csdnimg.cn/img_convert/7213ded01d686aab797968b2a6064358.png#pic_center 1.5x, https://img-blog.csdnimg.cn/img_convert/7213ded01d686aab797968b2a6064358.png#pic_center 2x"
        data-sizes="auto"
        alt="https://img-blog.csdnimg.cn/img_convert/7213ded01d686aab797968b2a6064358.png#pic_center"
        title="C&#43;&#43;编译期反射——以AOP为例" /></div><div class="single-card" data-image="true"><h2 class="single-title animated flipInX">C&#43;&#43;编译期反射——以AOP为例</h2><div class="post-meta">
                <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>作者</a></span>&nbsp;<span class="post-category">出版于  <a href="/categories/c++%E5%85%83%E6%A8%A1%E6%9D%BF%E7%BC%96%E7%A8%8B/"><i class="far fa-folder fa-fw"></i>C++元模板编程</a></span></div>
                <div class="post-meta-line"><span><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2022-11-25">2022-11-25</time></span>&nbsp;<span><i class="fas fa-pencil-alt fa-fw"></i>&nbsp;约 3628 字</span>&nbsp;
                    <span><i class="far fa-clock fa-fw"></i>&nbsp;预计阅读 8 分钟</span>&nbsp;</div>
            </div>
            
            <hr><div class="details toc" id="toc-static"  data-kept="">
                    <div class="details-summary toc-title">
                        <span>目录</span>
                        <span><i class="details-icon fas fa-angle-right"></i></span>
                    </div>
                    <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#获取类的方法">获取类的方法</a>
      <ul>
        <li><a href="#判断类是否具有某方法">判断类是否具有某方法</a></li>
        <li><a href="#将类方法转为function保存">将类方法转为function保存</a></li>
      </ul>
    </li>
    <li><a href="#aop的实现">AOP的实现</a>
      <ul>
        <li><a href="#invoke调用实现aop">Invoke调用实现AOP</a></li>
        <li><a href="#统一转function存储并实现aop调用顺序">统一转function存储并实现AOP调用顺序</a></li>
      </ul>
    </li>
  </ul>
</nav></div>
                </div><div class="content" id="content"><h1 id="编译期反射实践">编译期反射实践</h1>
<p>自古以来，C++就一直缺少一个编程语言的重要特性——反射，但如果熟悉C++元模板编程的同学，就知道以C++的风格，肯定是不会在标准库中添加运行时的反射支持的，从最新的C++版本演进来看，倒是编译期反射可能得到更好的支持。C++11 -&gt; C++14 -&gt; C++17 -&gt; C++20&hellip; 不断让元模板编程变得更简单，更规范。</p>
<p>本次的编译期反射实践，代码要求的最低C++版本为14，因为用到了 make_shared、decay_t。</p>
<p>本次实践的完整代码仓库：<a href="https://github.com/ACking-you/MyUtil/tree/master/aop" target="_blank" rel="noopener noreffer">MyUtil/tree/master/aop</a></p>
<h2 id="获取类的方法">获取类的方法</h2>
<h3 id="判断类是否具有某方法">判断类是否具有某方法</h3>
<p>我们如何判断某个类是否具有某个方法呢？</p>
<p>要想在编译期间实现这样一个判断，我们的思路可以是这样：写两个模板，如果这个类型具有这个方法，就匹配到返回 <code>std::true_type()</code> 的模板，如果不具备则匹配到返回 <code>std::false_type()</code> 的模板，最后通过 <code>std::is_same</code> 能够判断匹配结果，也就是实现了在编译期间判断类是否有这个方法。</p>
<p>上述过程，利用 <a href="https://en.cppreference.com/w/cpp/language/sfinae" target="_blank" rel="noopener noreffer">SFINAE</a> 的原理可以轻松实现，如果不了解 SFINAE 以及对应的 enable_if 的运用，可以看看这篇文章：<a href="https://zhuanlan.zhihu.com/p/21314708" target="_blank" rel="noopener noreffer">C++模板进阶指南：SFINAE</a>。</p>
<p>我们现在就开始动手实现上述代码，假设我们需要判断一个类型是否有 <code>before()</code> 方法。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Args</span><span class="o">&gt;</span> 
<span class="k">struct</span> <span class="nc">has_member_before</span> <span class="p">{</span>         
  <span class="k">private</span><span class="o">:</span>                                                                     
    <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">U</span><span class="o">&gt;</span>                                                      
    <span class="k">static</span> <span class="k">auto</span> <span class="n">Check</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span> 
      <span class="o">-&gt;</span> <span class="k">decltype</span><span class="p">(</span>
        <span class="n">std</span><span class="o">::</span><span class="n">declval</span><span class="o">&lt;</span><span class="n">U</span><span class="o">&gt;</span><span class="p">().</span><span class="n">before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">declval</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">()...),</span><span class="n">std</span><span class="o">::</span><span class="n">true_type</span><span class="p">()</span> <span class="c1">//1
</span><span class="c1"></span>    <span class="p">);</span>                                         
    <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">U</span><span class="o">&gt;</span> <span class="k">static</span> <span class="n">std</span><span class="o">::</span><span class="n">false_type</span> <span class="n">Check</span><span class="p">(...);</span> <span class="c1">//2                   
</span><span class="c1"></span>                                                                               
  <span class="k">public</span><span class="o">:</span>                                                                      
    <span class="k">enum</span> <span class="p">{</span>                                                                     
      <span class="n">value</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o">&lt;</span><span class="k">decltype</span><span class="p">(</span><span class="n">Check</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="mi">0</span><span class="p">)),</span> <span class="n">std</span><span class="o">::</span><span class="n">true_type</span><span class="o">&gt;::</span><span class="n">value</span> <span class="c1">//3      
</span><span class="c1"></span>    <span class="p">};</span>                                                                         
<span class="p">};</span>
</code></pre></div><p>先讲下上述代码定义后如何使用吧，比如现在有个 Student 类型，我们来判断是否具有 before 成员函数，则只需要写下下面的代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="n">has_member_before</span><span class="o">&lt;</span><span class="n">Student</span><span class="p">,</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">value</span> <span class="c1">//判断Student类是否有Student::before(int)方法
</span></code></pre></div><p>上面的代码重点有三段，已经作为标记1、2、3：</p>
<p>代码1处，利用了 <code>std::declval</code> 在编译期创建类型U的实例，并调用其 <code>before</code> 方法，这是在元模板中判断一个类型是否具有某个方法的常有手段，因为 SFINAE 的存在，即便该处替换出错，编译器会去继续寻找下一个替换是否能够正确，直到所有的替换都出错。</p>
<p>很明显这里是一定会替换成功的，因为代码2有一个包容性很强的重载，这个重载的参数不能和代码1处的重载参数一致，否则会算作重复定义，当然如果你使用 <code>std::enable_if</code> 对参数一致的模板参数进行唯一性的限制，那么重复定义的错误也可以避免。但是写成 C 的可变参数是最快的解决方式。</p>
<p>代码1处，有个逗号表达式的细节，如果成功被代码1处替换，那么返回值类型将会是 <code>decltype()</code> 中的表达式类型，也就是逗号表达式最后的结果 <code>std::true_type</code>。</p>
<p>代码3是利用enum类型在编译期得到具体的常量值。具体是通过调用  <code>Check&lt;T&gt;(0)</code> 获取该函数的返回值类型，这期间模板的匹配替换就会牵扯到前面的代码1、2。所以一旦模板被实例化，那么该class是否具有该方法的信息也就清楚了。</p>
<p>最后我们可以把该段代码提取为宏作为通用代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="cp">#define HAS_MEMBER(member)                                                     \
</span><span class="cp">  template &lt;typename T, typename... Args&gt; struct has_member_##member {         \
</span><span class="cp">  private:                                                                     \
</span><span class="cp">    template &lt;typename U&gt;                                                      \
</span><span class="cp">    static auto Check(int)                                                     \
</span><span class="cp">        -&gt; decltype(std::declval&lt;U&gt;().member(std::declval&lt;Args&gt;()...),         \
</span><span class="cp">                    std::true_type());                                         \
</span><span class="cp">    template &lt;typename U&gt; static std::false_type Check(...);                   \
</span><span class="cp">                                                                               \
</span><span class="cp">  public:                                                                      \
</span><span class="cp">    enum {                                                                     \
</span><span class="cp">      value = std::is_same&lt;decltype(Check&lt;T&gt;(0)), std::true_type&gt;::value       \
</span><span class="cp">    };                                                                         \
</span><span class="cp">  };
</span></code></pre></div><p>如果想要生成判断是否有before或者其他方法的代码，则只需要调用这个宏。</p>
<pre><code>HAS_MEMBER(before) //生成判断是否有before的代码
HAS_MEMBER(after) //生成判断是否有after的代码
</code></pre><h3 id="将类方法转为function保存">将类方法转为function保存</h3>
<p>直接上代码，再逐一讲解：</p>
<p>以下代码是将该类的before和after方法包装成一个function，并返回一个pair。完整代码：<a href="https://github.com/ACking-you/MyUtil/blob/master/aop/reflect_util.hpp" target="_blank" rel="noopener noreffer">reflect_util.hpp</a></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Args</span><span class="o">&gt;</span>
<span class="k">typename</span> <span class="n">std</span><span class="o">::</span><span class="n">enable_if</span><span class="o">&lt;</span> <span class="c1">//1
</span><span class="c1"></span>    <span class="n">has_member_before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span> <span class="n">has_member_after</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">,</span>
    <span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="n">Args</span><span class="p">...)</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="n">Args</span><span class="p">...)</span><span class="o">&gt;&gt;&gt;::</span><span class="n">type</span>
<span class="n">GetMemberFunc</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">auto</span> <span class="n">fun</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">make_shared</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">decay_t</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="p">();</span> <span class="c1">//2
</span><span class="c1"></span>  <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">make_pair</span><span class="p">(</span> <span class="c1">//3
</span><span class="c1"></span>      <span class="p">[</span><span class="n">self</span> <span class="o">=</span> <span class="n">fun</span><span class="p">](</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">self</span><span class="o">-&gt;</span><span class="n">before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
      <span class="p">},</span>
      <span class="p">[</span><span class="n">self</span> <span class="o">=</span> <span class="n">fun</span><span class="p">](</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">self</span><span class="o">-&gt;</span><span class="n">after</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
      <span class="p">});</span>
<span class="p">}</span>
</code></pre></div><p>在代码段1中，通过 <code>enable_if</code> 确保在该类型有before和after方法，同时也可以保证写其他版本的时候不会出现重复定义的错误。<code>enable_if</code> 第一个参数是需要满足的条件，第二个参数是enable_if内部的type类型，默认为void。</p>
<p>代码段2中，创建一个T类型的实例，并用shread_ptr管理，原因在于before方法和after方法需要共用内存，而这两个方法都要被提取为单独的function，要保证内存安全，故需要使用智能指针。其中 <code>std::decay_t&lt;T&gt;</code> 效果等同于 <code>std::decay&lt;T&gt;::type</code>，作用是消除T类型的const修饰和引用修饰。因为make_shared&lt;&gt;中的模板参数不能为引用类型。</p>
<p>代码段3中，利用lamda表达式将fun拷贝一份到其中命名为self，最后返回pair即可。</p>
<p>当前写的功能是不完整的，需要多几个模板的重载来实现只有before方法、以及只有after方法的情况。写法和上述代码一致，只不过 <code>enable_if</code> 中的条件稍作改变即可。前面也提到过enable_if千万不能丢，否则会报重复定义的错误，当然如果你是C++17的版本，可以直接使用 <code>if constexpr</code> 来实现更为简洁的代码而无需单独写三个函数。</p>
<p>如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="cp">#define ST_ASSERT                                                              \
</span><span class="cp">  static_assert(                                                               \
</span><span class="cp">      has_member_before&lt;T, Args...&gt;::value ||                                  \
</span><span class="cp">          has_member_after&lt;T, Args...&gt;::value,                                 \
</span><span class="cp">      &#34;class need T::before(args...) or T::after(args...) member function!&#34;);
</span><span class="cp"></span>
<span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Args</span><span class="o">&gt;</span>
<span class="n">std</span><span class="o">::</span><span class="n">pair</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="n">Args</span><span class="p">...)</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">function</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">(</span><span class="n">Args</span><span class="p">...)</span><span class="o">&gt;&gt;</span>
<span class="n">GetMemberFunc</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">ST_ASSERT</span> <span class="c1">// 确保至少before after有其一
</span><span class="c1"></span>      <span class="k">auto</span> <span class="n">fun</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">make_shared</span><span class="o">&lt;</span><span class="n">std</span><span class="o">::</span><span class="n">decay_t</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="p">();</span>
  <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">has_member_before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span>
                <span class="n">has_member_after</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 有before和after
</span><span class="c1"></span>    <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">make_pair</span><span class="p">(</span>
        <span class="p">[</span><span class="n">self</span> <span class="o">=</span> <span class="n">fun</span><span class="p">](</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">self</span><span class="o">-&gt;</span><span class="n">before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
        <span class="p">},</span>
        <span class="p">[</span><span class="n">self</span> <span class="o">=</span> <span class="n">fun</span><span class="p">](</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">self</span><span class="o">-&gt;</span><span class="n">after</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
        <span class="p">});</span>
  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">has_member_before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span>
                       <span class="o">!</span><span class="n">has_member_after</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 有before
</span><span class="c1"></span>    <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">make_pair</span><span class="p">(</span>
        <span class="p">[</span><span class="n">self</span> <span class="o">=</span> <span class="n">fun</span><span class="p">](</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
          <span class="n">self</span><span class="o">-&gt;</span><span class="n">before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
        <span class="p">},</span>
        <span class="k">nullptr</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// 只有after
</span><span class="c1"></span>    <span class="k">return</span> <span class="n">std</span><span class="o">::</span><span class="n">make_pair</span><span class="p">(</span><span class="k">nullptr</span><span class="p">,</span> <span class="p">[</span><span class="n">self</span> <span class="o">=</span> <span class="n">fun</span><span class="p">](</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">self</span><span class="o">-&gt;</span><span class="n">after</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
    <span class="p">});</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>下面我简单解释下代码：</p>
<ol>
<li>ST_ASSERT宏的作用是，通过static_assert在编译期抛出提示，T类型必须有before或after两个方法之一。</li>
<li>通过该类型拥有的情况不同，给出不同的返回值。</li>
</ol>
<p>很明显去除了enable_if后，我们代码清爽了许多。</p>
<h2 id="aop的实现">AOP的实现</h2>
<p>关于AOP，大家可以去搜一搜，这里就不过多赘述。我的简单理解就是一个事件回调，可以嵌入到业务的执行前后，把这个事件的概念换成一个切面，把业务代码看作一个横向坐标轴上的面，那么AOP就是在这个面的前后添加其他切面来实现常用的业务复用。比如用户的身份验证，可以在业务之前添加身份验证的切面，比如需要测试该业务的性能，那么可以在业务切面的前后添加开始计时和终止计时的逻辑。</p>
<h3 id="invoke调用实现aop">Invoke调用实现AOP</h3>
<p>根据上述对AOP的描述，我们要切入的代码主要是前和后两个逻辑，故每个要切入的类可以规定他必须定义Before或者After方法。然后通过可变参模板递归实现任意个参数的切面调用。</p>
<p>可以把整个切面调用过程看作一个洋葱圈层，比如添加s1类型的before和after作为切片，s2类型的before和after作为切片，s3类型的before作为切片。把业务代码逻辑作为foo函数。</p>
<p>则他们的调用过程如下：</p>
<p>s1-&gt;before =&gt; s2-&gt;before =&gt; s3-&gt;before =&gt; foo业务逻辑 =&gt; s1-&gt;after =&gt; s2-&gt;after。</p>
<p>如果稍微学过点数据结果，这个调用就能想到前中后序遍历上去了。</p>
<p>代码实现如下（C++11需要使用eable_if来实现，代码量很多，所以这里就直接用C++17的 <code>if constexpr</code> 来实现了）：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"> <span class="cm">/*以下是截取的一个类的两个方法*/</span>

<span class="c1">// 递归的尽头
</span><span class="c1"></span>  <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="n">Invoke</span><span class="p">(</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">,</span> <span class="n">T</span> <span class="o">&amp;&amp;</span><span class="n">aspect</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ST_ASSERT</span>
    <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">has_member_Before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span>
                  <span class="n">has_member_After</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">aspect</span><span class="p">.</span><span class="n">Before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span> <span class="c1">// 核心逻辑之前的切面逻辑
</span><span class="c1"></span>      <span class="n">m_func</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>        <span class="c1">// 核心逻辑
</span><span class="c1"></span>      <span class="n">aspect</span><span class="p">.</span><span class="n">After</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span> <span class="c1">// 核心逻辑之后的切面逻辑
</span><span class="c1"></span>    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">has_member_Before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span>
                         <span class="o">!</span><span class="n">has_member_After</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">aspect</span><span class="p">.</span><span class="n">Before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span> <span class="c1">// 核心逻辑之前的切面逻辑
</span><span class="c1"></span>      <span class="n">m_func</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>        <span class="c1">// 核心逻辑
</span><span class="c1"></span>    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">m_func</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>       <span class="c1">// 核心逻辑
</span><span class="c1"></span>      <span class="n">aspect</span><span class="p">.</span><span class="n">After</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span> <span class="c1">// 核心逻辑之后的切面逻辑
</span><span class="c1"></span>    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// 变参模板递归
</span><span class="c1"></span>  <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Tail</span><span class="o">&gt;</span>
  <span class="kt">void</span> <span class="n">Invoke</span><span class="p">(</span><span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">,</span> <span class="n">T</span> <span class="o">&amp;&amp;</span><span class="n">headAspect</span><span class="p">,</span> <span class="n">Tail</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">tailAspect</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ST_ASSERT</span>
    <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">has_member_Before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span>
                  <span class="n">has_member_After</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">headAspect</span><span class="p">.</span><span class="n">Before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
      <span class="n">Invoke</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...,</span> <span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Tail</span><span class="o">&gt;</span><span class="p">(</span><span class="n">tailAspect</span><span class="p">)...);</span>
      <span class="n">headAspect</span><span class="p">.</span><span class="n">After</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="nf">constexpr</span> <span class="p">(</span><span class="n">has_member_Before</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span> <span class="o">&amp;&amp;</span>
                         <span class="o">!</span><span class="n">has_member_After</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;::</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">headAspect</span><span class="p">.</span><span class="n">Before</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span>
      <span class="n">Invoke</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...,</span> <span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Tail</span><span class="o">&gt;</span><span class="p">(</span><span class="n">tailAspect</span><span class="p">)...);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">Invoke</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...,</span> <span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Tail</span><span class="o">&gt;</span><span class="p">(</span><span class="n">tailAspect</span><span class="p">)...);</span>
      <span class="n">headAspect</span><span class="p">.</span><span class="n">After</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span> <span class="c1">// 核心逻辑之后的切面逻辑
</span><span class="c1"></span>    <span class="p">}</span>
  <span class="p">}</span>
</code></pre></div><p>上述完整代码：<a href="https://github.com/ACking-you/MyUtil/blob/master/aop/aspect.hpp" target="_blank" rel="noopener noreffer">aspect.hpp</a></p>
<p>上述代码是根据C++变参模板实现的通用性操作，可以同时添加多个切片 ，他们都是Aspect类的两个方法，具体实现逻辑就是：通过之前得到的编译期常量( <code>has_member_Before&lt;T,Args...&gt;::value</code> )判断 T 是否具有Before或者After方法，分三种情况：</p>
<ol>
<li>
<p>同时又Before和After：利用中序进行递归。</p>
</li>
<li>
<p>只有Before：利用前序进行递归。</p>
</li>
<li>
<p>只有After：利用后序进行递归。</p>
</li>
</ol>
<blockquote>
<p>为了简化调用过程，继续封装如下：</p>
</blockquote>
<p>最后记得定义一个终止模板递归的最终形态。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="o">&gt;</span> <span class="k">using</span> <span class="n">identity_t</span> <span class="o">=</span> <span class="n">T</span><span class="p">;</span>

<span class="c1">// AOP的辅助函数，简化调用
</span><span class="c1"></span><span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span><span class="p">...</span> <span class="n">AP</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Args</span><span class="p">,</span> <span class="k">typename</span> <span class="n">Func</span><span class="o">&gt;</span>
<span class="kt">void</span> <span class="n">Invoke</span><span class="p">(</span><span class="n">Func</span> <span class="o">&amp;&amp;</span><span class="n">f</span><span class="p">,</span> <span class="n">Args</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">args</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">Aspect</span><span class="o">&lt;</span><span class="n">Func</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;</span> <span class="n">asp</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Func</span><span class="o">&gt;</span><span class="p">(</span><span class="n">f</span><span class="p">));</span>
  <span class="n">asp</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o">&lt;</span><span class="n">Args</span><span class="o">&gt;</span><span class="p">(</span><span class="n">args</span><span class="p">)...,</span> <span class="n">identity_t</span><span class="o">&lt;</span><span class="n">AP</span><span class="o">&gt;</span><span class="p">()...);</span>
<span class="p">}</span>
</code></pre></div><p>最终如果像最开始讲的要拓展s1、s2、s3的方法上去，那么简单的使用如下代码即可：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="n">Invoke</span><span class="o">&lt;</span><span class="n">s1</span><span class="p">,</span><span class="n">s2</span><span class="p">,</span><span class="n">s3</span><span class="o">&gt;</span><span class="p">(</span><span class="o">&amp;</span><span class="n">foo</span><span class="p">,</span><span class="n">args</span><span class="p">);</span> <span class="c1">//s1,s2,s3为拓展逻辑，foo为业务逻辑
</span></code></pre></div><h3 id="统一转function存储并实现aop调用顺序">统一转function存储并实现AOP调用顺序</h3>
<h4 id="统一转function存储">统一转function存储</h4>
<p>将任意类的before和after方法集体装箱为function的关键代码逻辑如下，完整代码请看：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp">  <span class="kt">void</span> <span class="nf">Get</span><span class="p">()</span> <span class="p">{}</span> <span class="c1">//空的func，用于结束模板的递归实例化
</span><span class="c1"></span>
  <span class="k">template</span> <span class="o">&lt;</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="k">typename</span><span class="p">...</span> <span class="n">Tail</span><span class="o">&gt;</span> <span class="kt">void</span> <span class="n">Get</span><span class="p">(</span><span class="n">T</span> <span class="o">&amp;&amp;</span><span class="n">head</span><span class="p">,</span> <span class="n">Tail</span> <span class="o">&amp;&amp;</span><span class="p">...</span><span class="n">tails</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">ST_ASSERT</span>
    <span class="k">auto</span> <span class="o">&amp;&amp;</span><span class="n">p</span> <span class="o">=</span> <span class="n">details</span><span class="o">::</span><span class="n">GetMemberFunc</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span> <span class="n">Args</span><span class="p">...</span><span class="o">&gt;</span><span class="p">();</span>
    <span class="n">m_output</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
    <span class="n">Get</span><span class="p">(</span><span class="n">tails</span><span class="p">...);</span>
  <span class="p">}</span>
</code></pre></div><p>由于所有的获取before和after的逻辑在前面<strong>获取类的方法</strong>已经讲到，所以单个类型直接调用 <code>GetMemberFunc</code> 函数即可得出结果，并放入vector中，最后通过模板实例化的递归将所有的类型都装箱。</p>
<p>具体的使用方式也很简单，如下代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="cp">#include</span><span class="cpf">&#34;reflect_util.hpp&#34;</span><span class="cp">
</span><span class="cp"></span><span class="k">using</span> <span class="n">func_t</span> <span class="o">=</span> <span class="n">reflect</span><span class="o">::</span><span class="n">MemberFunc</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">func_t</span><span class="p">;</span>
<span class="k">using</span> <span class="n">func_pair_t</span> <span class="o">=</span> <span class="n">reflect</span><span class="o">::</span><span class="n">MemberFunc</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;::</span><span class="n">func_pair_t</span><span class="p">;</span>

<span class="k">struct</span> <span class="nc">LoginAspect</span> <span class="p">{</span>
  <span class="kt">void</span> <span class="nf">before</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Login start &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>
  <span class="kt">void</span> <span class="nf">after</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span> <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;after start &#34;</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
   <span class="n">vector</span><span class="o">&lt;</span><span class="n">func_pair_t</span><span class="o">&gt;</span> <span class="n">out</span><span class="p">;</span>
  <span class="c1">// 获取before和after方法，并通过function进行包装
</span><span class="c1"></span>  <span class="n">reflect</span><span class="o">::</span><span class="n">MemberFunc</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span><span class="p">(</span><span class="n">out</span><span class="p">).</span><span class="n">Get</span><span class="p">(</span>
      <span class="n">TimeElapsedAspect</span><span class="p">{},</span> 
      <span class="n">LoggingAspect</span><span class="p">{},</span>
      <span class="n">LoginAspect</span><span class="p">{}</span>
  <span class="p">);</span>
  <span class="c1">//将三个类型的before和after方法分离成function后以pair的形式存储在out中
</span><span class="c1"></span>  <span class="k">for</span><span class="p">(</span><span class="k">auto</span><span class="o">&amp;&amp;</span> <span class="nl">p</span> <span class="p">:</span> <span class="n">out</span><span class="p">){</span>
      <span class="k">if</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">first</span><span class="p">){</span><span class="c1">//如果before存在则调用
</span><span class="c1"></span>          <span class="n">p</span><span class="p">.</span><span class="n">first</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="k">if</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">second</span><span class="p">){</span><span class="c1">//如果after存在则调用
</span><span class="c1"></span>          <span class="n">p</span><span class="p">.</span><span class="n">second</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
      <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div><h4 id="aop的调用顺序实现">AOP的调用顺序实现</h4>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cpp" data-lang="cpp"><span class="c1">// 根据AOP的顺序存入out数组
</span><span class="c1"></span><span class="kt">void</span> <span class="nf">AspectOrder</span><span class="p">(</span><span class="n">vector</span><span class="o">&lt;</span><span class="n">func_t</span><span class="o">&gt;</span> <span class="o">&amp;</span><span class="n">out</span><span class="p">,</span> <span class="n">vector</span><span class="o">&lt;</span><span class="n">func_pair_t</span><span class="o">&gt;</span> <span class="o">&amp;</span><span class="n">v</span><span class="p">,</span>
                    <span class="k">const</span> <span class="n">func_t</span> <span class="o">&amp;</span><span class="n">func</span><span class="p">,</span> <span class="kt">int</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">&lt;=</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">out</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">func</span><span class="p">);</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">index</span><span class="p">].</span><span class="n">first</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">out</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">index</span><span class="p">].</span><span class="n">first</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="n">AspectOrder</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">index</span><span class="p">].</span><span class="n">second</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">out</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">v</span><span class="p">[</span><span class="n">index</span><span class="p">].</span><span class="n">second</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div><p>完整测试代码：<a href="https://github.com/ACking-you/MyUtil/blob/master/aop/test_aspect.cc" target="_blank" rel="noopener noreffer">test_aspect.cc</a></p>
</div><div class="post-footer" id="post-footer">
    <div class="post-info"><div class="post-info-tag"><span><a href="/tags/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/">C&#43;&#43;编译期反射——以AOP为例</a>
                </span></div><div class="post-info-line"><div class="post-info-mod">
                <span>更新于 2022-11-25</span>
            </div><div class="post-info-mod"></div>
        </div><div class="post-info-share">
            <span><a href="javascript:void(0);" title="分享到 Twitter" data-sharer="twitter" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例" data-hashtags="C&#43;&#43;编译期反射——以AOP为例"><i class="fab fa-twitter fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Facebook" data-sharer="facebook" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-hashtag="C&#43;&#43;编译期反射——以AOP为例"><i class="fab fa-facebook-square fa-fw"></i></a><a href="javascript:void(0);" title="分享到 WhatsApp" data-sharer="whatsapp" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例" data-web><i class="fab fa-whatsapp fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Line" data-sharer="line" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例"><i class="fab fa-line fa-fw"></i></a><a href="javascript:void(0);" title="分享到 微博" data-sharer="weibo" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例" data-image="https://img-blog.csdnimg.cn/img_convert/7213ded01d686aab797968b2a6064358.png#pic_center"><i class="fab fa-weibo fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Myspace" data-sharer="myspace" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例" data-description="C&#43;&#43;编译期反射——以AOP为例"><i data-svg-src="/lib/simple-icons/icons/myspace.min.svg"></i></a><a href="javascript:void(0);" title="分享到 Blogger" data-sharer="blogger" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例" data-description="C&#43;&#43;编译期反射——以AOP为例"><i class="fab fa-blogger fa-fw"></i></a><a href="javascript:void(0);" title="分享到 Evernote" data-sharer="evernote" data-url="https://acking-you.github.io/posts/c&#43;&#43;%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%8D%E5%B0%84%E4%BB%A5aop%E4%B8%BA%E4%BE%8B/" data-title="C&#43;&#43;编译期反射——以AOP为例"><i class="fab fa-evernote fa-fw"></i></a></span>
        </div></div><div class="post-nav"><a href="/posts/go%E4%B8%9A%E5%8A%A1%E4%BB%A3%E7%A0%81%E4%B8%80%E9%94%AE%E9%80%86%E5%90%91%E7%94%9F%E6%88%90/" class="prev" rel="prev" title="go语言业务代码一键逆向生成"><i class="fas fa-angle-left fa-fw"></i>Previous Post</a>
            <a href="/posts/%E6%AF%94%E8%BE%83avl%E6%A0%91%E5%92%8C%E7%BA%A2%E9%BB%91%E6%A0%91%E7%9A%84%E6%80%A7%E8%83%BD%E5%B7%AE%E5%BC%82/" class="next" rel="next" title="比较AVL树和红黑树的性能差异">Next Post<i class="fas fa-angle-right fa-fw"></i></a></div></div>
</div></article></div>
            </main>
            <footer class="footer"><div class="footer-container"><div class="footer-line">由 <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.86.0">Hugo</a> 强力驱动 | 主题 - <a href="https://github.com/khusika/FeelIt" target="_blank" rel="noopener noreffer" title="FeelIt 1.0.1"><i class="fas fa-hand-holding-heart fa-fw"></i> FeelIt</a>
        </div><div class="footer-line" itemscope itemtype="http://schema.org/CreativeWork"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2023</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/"></a></span></div>
</div>
</footer>
        </div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="回到顶部">
                <i class="fas fa-chevron-up fa-fw"></i>
            </a></div><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><link rel="stylesheet" href="/lib/katex/katex.min.css"><link rel="stylesheet" href="/lib/katex/copy-tex.min.css"><script src="/lib/autocomplete/autocomplete.min.js"></script><script src="/lib/lunr/lunr.min.js"></script><script src="/lib/lunr/lunr.stemmer.support.min.js"></script><script src="/lib/lunr/lunr.zh.min.js"></script><script src="/lib/lazysizes/lazysizes.min.js"></script><script src="/lib/clipboard/clipboard.min.js"></script><script src="/lib/sharer/sharer.min.js"></script><script src="/lib/katex/katex.min.js"></script><script src="/lib/katex/auto-render.min.js"></script><script src="/lib/katex/copy-tex.min.js"></script><script src="/lib/katex/mhchem.min.js"></script><script>window.config={"code":{"copyTitle":"复制到剪贴板","maxShownLines":200},"comment":{},"math":{"delimiters":[{"display":true,"left":"$$","right":"$$"},{"display":true,"left":"\\[","right":"\\]"},{"display":false,"left":"$","right":"$"},{"display":false,"left":"\\(","right":"\\)"}],"strict":false},"search":{"highlightTag":"em","lunrIndexURL":"/index.json","lunrLanguageCode":"zh","lunrSegmentitURL":"/lib/lunr/lunr.segmentit.js","maxResultLength":100,"noResultsFound":"没有找到结果","snippetLength":50,"type":"lunr"}};</script><script src="/js/theme.min.js"></script></body></html>
